import { encrypt, decrypt } from 'crypto-js/aes';
import { parse } from 'crypto-js/enc-utf8';
import pkcs7 from 'crypto-js/pad-pkcs7';
import ECB from 'crypto-js/mode-ecb';
import md5 from 'crypto-js/md5';
import UTF8 from 'crypto-js/enc-utf8';
import Base64 from 'crypto-js/enc-base64';
import JSEncrypt from 'jsencrypt';
import Encrypt from 'encryptlong';
import forge from 'node-forge';
import { SM4, SM3, SM2 } from 'gm-crypto';
import { nanoid } from 'nanoid';
import { hextob64, b64tohex, KJUR } from 'jsrsasign'; // base64转16进制 验签所需插件

export interface AesEncryptionParams {
  key: string;
  iv: string;
}

export class AesEncryption {
  private key;
  private iv;

  constructor(opt: Partial<AesEncryptionParams> = {}) {
    const { key, iv } = opt;
    if (key) {
      this.key = Base64.parse(key);
    }
    if (iv) {
      this.iv = Base64.parse(iv);
    }
  }

  get getOptions() {
    return {
      mode: ECB,
      padding: pkcs7,
      iv: this.iv,
    };
  }

  encrypt(cipherText: string) {
    const cipher = encrypt(cipherText, this.key, this.getOptions);
    // 将加密后的数据转换成 Base64
    const base64Cipher = Base64.stringify(cipher.ciphertext);
    // 返回加密后的经过处理的 Base64
    return base64Cipher;
  }

  decrypt(cipherText: string) {
    const decipher = decrypt(cipherText, this.key, this.getOptions);
    // 将解密对象转换成 UTF8 的字符串
    const resultDecipher = UTF8.stringify(decipher);
    // 返回解密结果
    return resultDecipher;
  }

  //随机生成指定数量的16进制key
  generatekey() {
    const num = 16;
    const library = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let key = '';
    for (let i = 0; i < num; i++) {
      const randomPoz = Math.floor(Math.random() * library.length);
      key += library.substring(randomPoz, randomPoz + 1);
    }
    const wordArray = UTF8.parse(key);
    return Base64.stringify(wordArray);
  }
}

//https://gitcode.net/mirrors/byte-fe/gm-crypto?utm_source=csdn_github_accelerator
export class SM4Encryption {
  private key;
  private iv;

  constructor(opt: Partial<AesEncryptionParams> = {}) {
    const { key, iv } = opt;
    if (key) {
      this.key = parse(key);
    }
    if (iv) {
      this.iv = parse(iv);
    }
  }

  encrypt(cipherText: string) {
    const encryptedData = SM4.encrypt(cipherText, this.key, {
      inputEncoding: 'base64',
      outputEncoding: 'base64',
      iv: this.iv,
    });
    return encryptedData;
  }

  decrypt(cipherText: string) {
    const decryptedData = SM4.decrypt(cipherText, this.key, {
      inputEncoding: 'base64',
      outputEncoding: 'base64',
      iv: this.iv,
    });
    return decryptedData;
  }

  //随机生成指定数量的16进制key
  generatekey() {
    const keys = nanoid(16);
    return keys;
  }
}

export interface RsaEncryptionParams {
  publickey: string;
  privatekey: string;
}

export class RsaEncryption {
  private publickey;
  private privatekey;

  constructor(opt: Partial<RsaEncryptionParams> = {}) {
    const { publickey, privatekey } = opt;
    if (publickey) {
      this.publickey = publickey;
    }
    if (privatekey) {
      this.privatekey = privatekey;
    }
  }

  encrypt(cipherText: string) {
    const decrypt = new JSEncrypt();
    decrypt.setPublicKey(this.publickey);
    return decrypt.encrypt(cipherText).toString();
  }

  decrypt(cipherText: string) {
    const decrypt = new JSEncrypt();
    decrypt.setPrivateKey(this.privatekey);
    return decrypt.decrypt(cipherText).toString();
  }

  encryptLong(cipherText: string) {
    const decrypt = new Encrypt();
    decrypt.setPublicKey(this.publickey);
    return decrypt.encryptLong(cipherText);
  }

  decryptLong(cipherText: string) {
    const decrypt = new Encrypt();
    decrypt.setPrivateKey(this.privatekey);
    return decrypt.decryptLong(cipherText);
  }

  encryptForge(cipherText: string) {
    const publicKeyAll = '-----BEGIN PUBLIC KEY-----\n' + this.publickey + '\n-----END PUBLIC KEY-----';
    const publicKey = forge.pki.publicKeyFromPem(publicKeyAll);
    const buffer = forge.util.createBuffer(cipherText, 'utf8');
    const bytes = buffer.getBytes();
    const encrypted = forge.util.encode64(publicKey.encrypt(bytes, 'RSAES-PKCS1-V1_5'));
    return encrypted;
  }

  decryptForge(cipherText: string) {
    const privateKeyAll = '-----BEGIN RSA PRIVATE KEY-----\n' + this.privatekey + '\n-----END RSA PRIVATE KEY-----';
    const privateObj = forge.pki.privateKeyFromPem(privateKeyAll);
    const bytes = forge.util.decode64(cipherText);
    //const bytes = forge.util.bytesToHex(encrypted);
    //需要转换成 bytes 对象再解密
    const decrypted = privateObj.decrypt(bytes);
    return decrypted;
  }

  verify(plainText: string, signature: string) {
    const publicKeyAll = '-----BEGIN PUBLIC KEY-----\n' + this.publickey + '\n-----END PUBLIC KEY-----';
    const sig = new KJUR.crypto.Signature({ alg: 'MD5withRSA' });
    sig.init(publicKeyAll);
    sig.updateString(plainText);
    return sig.verify(b64tohex(signature));
  }

  sign(plainText: string) {
    const privateKeyAll = '-----BEGIN RSA PRIVATE KEY-----\n' + this.privatekey + '\n-----END RSA PRIVATE KEY-----';
    // 创建 Signature 对象
    const signature = new KJUR.crypto.Signature({
      alg: 'MD5withRSA', //私钥pem!
      prvkeypem: privateKeyAll,
    });
    signature.updateString(plainText);
    const signData = signature.sign(); // 将内容转成base64
    return hextob64(signData);
  }

  generatekey(): {
    privateKey: string;
    publicKey: string;
  } {
    const decrypt = new JSEncrypt();
    return { privateKey: decrypt.getPrivateKeyB64(), publicKey: decrypt.getPublicKeyB64() };
  }
}

export class SM2Encryption {
  private publickey;
  private privatekey;

  constructor(opt: Partial<RsaEncryptionParams> = {}) {
    const { publickey, privatekey } = opt;
    if (publickey) {
      this.publickey = publickey;
    }
    if (privatekey) {
      this.privatekey = privatekey;
    }
  }

  encrypt(cipherText: string) {
    const encryptedData = SM2.encrypt(cipherText, this.publickey, {
      inputEncoding: 'utf8',
      outputEncoding: 'base64',
    });
    return encryptedData;
  }

  decrypt(cipherText: string) {
    const decryptedData = SM2.decrypt(cipherText, this.privatekey, {
      inputEncoding: 'base64',
      outputEncoding: 'utf8',
    });
    return decryptedData;
  }

  verify(plainText: string, signature: string) {
    const sm2 = require('sm-crypto').sm2;
    return sm2.doVerifySignature(plainText, b64tohex(signature), this.publickey);
  }

  sign(plainText: string) {
    const sm2 = require('sm-crypto').sm2;
    const signData = sm2.doSignature(plainText, this.privatekey);
    return hextob64(signData);
  }

  generatekey(): {
    privateKey: string;
    publicKey: string;
  } {
    const keypair = SM2.generateKeyPair();
    return keypair;
  }
}

export function encryptByBase64(cipherText: string) {
  return UTF8.parse(cipherText).toString(Base64);
}

export function decodeByBase64(cipherText: string) {
  return Base64.parse(cipherText).toString(UTF8);
}

export function encryptByMd5(password: string) {
  return md5(password).toString();
}
